home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 28
/
Aminet 28 (1998)(GTI - Schatztruhe)[!][Dec 1998].iso
/
Aminet
/
dev
/
e
/
kyz_obj.lha
/
doc
/
patch.doc
< prev
next >
Wrap
Text File
|
1998-10-18
|
16KB
|
399 lines
TABLE OF CONTENTS
patch.m/--overview--
patch.m/disable
patch.m/enable
patch.m/end
patch.m/install
patch.m/missed
patch.m/patched_function
patch.m/remove
patch.m/--overview-- patch.m/--overview--
PURPOSE
To allow easy patching of Amiga library and device functions
with Amiga E code.
OVERVIEW
The AmigaOS has a facility to change the code called in any of the
functions of a library or device. But due to the way Amiga E
works, it has been difficult to easily use E code as patches.
This object allows you to perform patches on the system using
Amiga E code in the most flexible way, via an assembler wedge.
Installation:
You first need to open the library/device that you are going to
patch. You then need to know the Library Vector Offset of the
entry you are going to patch. This is available using the 'lvo'
tool from the Developer Kit, or any assembler include file with
'LVO' in the title. You must keep the library/device open for the
entire duration of the patch. If you do not, the library may be
flushed from memory and loaded somewhere else, indirectly removing
the patch and also causing a crash when we try to remove our
defunct patch later.
You create a patch object with install(), which installs a wedge
that calls an E function which you defined like so:
PROC patch_code(function, a7,a6,a5,a4,a3,a2,a1,a0,
d7,d6,d5,d4,d3,d2,d1,d0)
You can give alternate names to the 'register' parameters, but do
remember that they will always be passed IN THE ABOVE-STATED
ORDER, and writing the parameter's names in a different order will
NOT cause the registers to be passed to you in a different order.
See patched_function() for more about this.
Your patch in operation:
This E function is called 'as' the function you have patched, by
absolutely any task and program that could call the original
function. Also, the function may be called by more than one
program at once, so use a Semaphore or similar protection when
using global variables.
You are given all the registers as set when the patch was called -
do NOT modify them unless it is documented that the function does
this. That said, you can always modify the 'd0' variable as it is
ignored on exit, the return value of your function is returned in
D0 rather than the stack-based copy.
Occasionally you can modify the 'scratch register' variables d0,
d1, a0 and a1, but you must check the documentation of the
function you are patching, to ensure it does not promise to
preserve any of these registers.
The result of your function call is always returned in D0, but E's
multiple return values (in D1 and D2) are ignored, and restored to
their original values.
Calling the original function:
You are also given a pointer to the original function you have
patched. In most patches, you will have to call the original
function sometime, so you would set up the registers appropriately
from the parameters, including A6 as the library/device base, and
call the original function. Do NOT do this by writing the E
construct "function()", as this will use an unspecified A-register
to make the call. Instead, do it yourself with another register
that you choose. If you need to pass A0 to A3 as parameters, then
you will have to preserve and use A4 or A5.
Patch removal:
Your patch can be either enabled or disabled. When disabled, the
assembler wedge simply hops to the original function, adding
nothing to the stack. Simply exiting your program at this point
without ENDing the object would free the object instance itself
but would leave the working wedge in place, consuming 136 bytes of
memory and adding 3 instructions to the function.
Some people would advocate exiting your program with the wedge
still in place, as the user is unlikely to do that often, but when
they do there will be no problem with exiting correctly.
Others would always recommend total removal of the patch, even if
that means waiting. Unlike other wedges, the removal method used
by this object is very safe, only removes the patch if it is not
being used, and understands programs like SetFunction Manager or
SaferPatches which allow removal of patches in any order.
My advice is to always disable() the patch, then try to remove()
the patch. If that fails, ask the user if you should keep trying,
or just exit.
NOTES
You must NOT use Amiga E's 'debug' mode and EDBG on the code that
contains the patch you will be making. E introduces NOP commands
into the code, which EDBG turns to ILLEGAL commands, so it can
run the E code in the normal way, then give control back to itself
after each line. However, when the E code is called by other
tasks, these ILLEGALs are not trapped by EDBG, and simply crash
the task involved. A simple fix is to put the patch code in a
seperate module, and compile this without debug mode.
Exceptions should never be thrown out of patches. If there is the
possibility of an exception being raised, make the patch function
HANDLE it, not throw it out.
Some functions are marked as safe to be called from interrupts. If
this is the case, you have to handle the possible deadlock that
may occur - interrupt blocks a task, then goes into a loop that
waits for the blocked task to finish, which it will never do.
If the function you are patching is part of the AmigaOS, remember
to check the function's autodocs for defined side effects that you
must emulate.
You obviously cannot call the function you patched through the
usual way, but you also cannot call a system/library function that
calls your patched function as part of its operation.
You cannot perform I/O on _your_ streams from another process, and
tasks can't even call any DOS functions. Therefore, WriteF() in
your patch is out of the question. Use debug.m/kPrintF and Sushi.
WARNING
When your patch is enabled, the combination of assembler wedge and
E code will add a MINIMUM of 80 bytes - THEN extra bytes are added
for the variables in your patch!
You should minimise local variable usage - even register variables
add to stack usage - and avoid defining entire structures, STRINGs
or LISTs as local variables.
patch.m/disable patch.m/disable
NAME
patch.disable() -- prevent further execution of the patch.
SYNOPSIS
disable()
FUNCTION
Stops the patch from being invoked again. All calls to the patched
function will be passed directly to the original function, not to
your patch.
There may, however, still be invocations of the patch running at
the time this call returns.
SEE ALSO
enable()
patch.m/enable patch.m/enable
NAME
patch.enable() -- allow execution of the patch.
SYNOPSIS
enable()
FUNCTION
Toggles a switch in the assembler wedge which stops it passing all
calls of the patched function to the original function, and starts
passing them to your patch.
Your patch should be ready to run at any time from the start of
the call to this method.
SEE ALSO
disable()
patch.m/end patch.m/end
NAME
patch.end() -- Destructor.
SYNOPSIS
end()
FUNCTION
Frees resources used by an instance of the patch class. It will
first disable() the patch, then it will busy loop until the patch
is successfully removed.
SEE ALSO
disable(), remove(), install()
patch.m/install patch.m/install
NAME
patch.install() -- Constructor.
SYNOPSIS
install(base, offset, patchfunc)
install(base, offset, patchfunc, userdata)
install(base, offset, patchfunc, userdata, stackuse)
FUNCTION
Initialises an instance of the patch class, and installs a patch
in the system. The patch will not be enabled to begin with, so you
must call enable() on the patch for it to start working. The
exceptio